package org.datascooter.impl;

import java.lang.reflect.InvocationTargetException;
import java.text.ParseException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;

import org.datascooter.bundle.ContainerProvider;
import org.datascooter.bundle.CrossTable;
import org.datascooter.bundle.DSMapper;
import org.datascooter.bundle.EntityBundle;
import org.datascooter.bundle.EntityPart;
import org.datascooter.bundle.attribute.ContainerAttribute;
import org.datascooter.bundle.attribute.PersistAttribute;
import org.datascooter.bundle.attribute.PersistChildAttribute;
import org.datascooter.bundle.attribute.PersistencePolicy;
import org.datascooter.exception.DataManagerException;
import org.datascooter.exception.DataScooterException;
import org.datascooter.exception.EntityNotMappedException;
import org.datascooter.utils.SnipUtils;
import org.datascooter.utils.policy.SnipType;

/**
 * Purposed for operations of flush and instantiation of objects
 * 
 * @author nemo
 * 
 */
public class InstanceManager {

	private DataManager manager;
	private ContainerProvider containerProvider;

	public InstanceManager(DataManager manager, ContainerProvider containerProvider) {
		this.manager = manager;
		this.containerProvider = containerProvider;
	}

	/**
	 * Purposed for analysing object , breaking apart for further saving
	 * 
	 * @param <T>
	 * @param object
	 * @return DataSnip
	 * @throws WrongMethodException
	 * @throws EntityNotMappedException
	 */
	public <T> DataSnip flush(T object) {
		try {
			EntityBundle bundleIn = DSMapper.getBundle(object);
			DataSnip result = createInsertSnip(object, bundleIn);
			result.setBundle(bundleIn);
			if (bundleIn.hasPersistableChild) {
				flushPersistableChildren(bundleIn, object, result);
			}
			if (bundleIn.hasContainers) {
				flushContainers(bundleIn, object, result);
			}
			if (bundleIn.hasEmbedded) {
				flushEmbedded(bundleIn, object, result);
			}
			if (bundleIn.hasParts) {
				flushParts(bundleIn, object, result);
			}
			return result;
		} catch (Exception e) {
			throw new DataManagerException("***Error by flush item: " + object, e);
		}
	}

	public void provideChildrenLikeParent(DataSnip snip, Object obj) throws EntityNotMappedException, IllegalArgumentException, IllegalAccessException, InvocationTargetException, SecurityException, NoSuchMethodException {
		if ((obj instanceof String) || (obj instanceof Class<?>) || (obj instanceof DataSnip)) {
			return;
		}
		EntityBundle bundleIn = DSMapper.getBundle(snip.entityName);
		snip.setBundle(bundleIn);
		if (bundleIn.hasPersistableChild) {
			providePersistableChildren(bundleIn, snip, obj);
		}
		if (bundleIn.hasEmbedded) {
			provideEmbedded(bundleIn, snip, obj);
		}
		if (bundleIn.hasContainers) {
			provideContainers(bundleIn, snip, obj);
		}
		if (bundleIn.hasParts) {
			provideParts(bundleIn, snip, obj);
		}
	}

	private void provideEmbedded(EntityBundle bundleIn, DataSnip snip, Object obj) throws EntityNotMappedException, IllegalArgumentException, IllegalAccessException, InvocationTargetException, SecurityException, NoSuchMethodException {
		for (ContainerAttribute attribute : bundleIn.embeddedList) {
			if ((attribute.getPolicy().equals(PersistencePolicy.UPDATE) || attribute.getPolicy().equals(PersistencePolicy.ALL)) && snip.getSnipType().equals(SnipType.UPDATE)) {
				updateContentInt(snip, attribute, obj);
			}
		}
	}

	private void providePersistableChildren(EntityBundle bundleIn, DataSnip snip, Object obj) throws EntityNotMappedException, IllegalArgumentException, IllegalAccessException, InvocationTargetException, SecurityException, NoSuchMethodException {
		for (PersistChildAttribute childBundle : bundleIn.persistableChildList) {
			if ((childBundle.getPolicy().equals(PersistencePolicy.DELETE) || childBundle.getPolicy().equals(PersistencePolicy.ALL)) && snip.getSnipType().equals(SnipType.DELETE)) {
				if (!childBundle.isResolved()) {
					childBundle.resolveReferrer();
				}
				DataSnip dataSnip = DataSnipFactory.none(childBundle.getEntity());
				dataSnip.setSnipType(snip.getSnipType());
				if (childBundle.isParent()) {
					Object parentIdEx = childBundle.getReference().getValue(obj);
					dataSnip.whereId(parentIdEx);
				} else {
					dataSnip.where(childBundle.getJoinAttribute(), DSMapper.getBundle(obj).getId(obj));
				}
				snip.fetch(dataSnip);
			}
		}
	}

	private void provideContainers(EntityBundle bundleIn, DataSnip snip, Object obj) throws EntityNotMappedException, IllegalArgumentException, IllegalAccessException, InvocationTargetException, SecurityException, NoSuchMethodException {
		for (ContainerAttribute attribute : bundleIn.containerAttributeList) {
			if ((attribute.getPolicy().equals(PersistencePolicy.DELETE) || attribute.getPolicy().equals(PersistencePolicy.ALL)) && snip.getSnipType().equals(SnipType.DELETE)) {
				removeContentInt(snip, attribute, obj);
			}
		}
	}

	public void removeContentInt(DataSnip snip, ContainerAttribute attribute, Object obj) throws EntityNotMappedException, IllegalArgumentException, IllegalAccessException, InvocationTargetException, SecurityException, NoSuchMethodException {
		CrossTable crossTable = containerProvider.getCrossTable(snip, attribute.name, obj);
		if (crossTable == null) {
			return;
		}
		DataSnip dataSnip = DataSnipFactory.none(crossTable.getTableName());
		EntityBundle bundle = DSMapper.getBundle(obj);
		if (bundle == null) {
			return;
		}
		try {
			dataSnip.where(ContainerProvider.CONTAINER_ID, bundle.getId(obj));
		} catch (Exception e) {
			e.printStackTrace();
		}
		dataSnip.setSnipType(snip.getSnipType());
		snip.fetch(dataSnip);
		Object value = attribute.getValue(obj);
		if (Collection.class.isAssignableFrom(value.getClass())) {
			Collection<?> collection = (Collection<?>) value;
			if (collection.size() > 0) {
				Iterator<?> iterator = collection.iterator();
				while (iterator.hasNext()) {
					Object next = iterator.next();
					addSnip(snip, attribute, next);
				}
			}
		} else if (Map.class.isAssignableFrom(value.getClass())) {
			Map<?, ?> map1 = (Map<?, ?>) value;
			if (map1.size() > 0) {
				Iterator<?> iterator = map1.values().iterator();
				while (iterator.hasNext()) {
					Object next = iterator.next();
					addSnip(snip, attribute, next);
				}
			}
		} else if (value.getClass().isArray()) {
			Object[] array = (Object[]) value;
			if (array.length > 0) {
				for (int a = 0; a < array.length; a++) {
					Object next = array[a];
					addSnip(snip, attribute, next);
				}
			}
		} else {
			addSnip(snip, attribute, value);
		}
	}

	private void addSnip(DataSnip snip, ContainerAttribute attribute, Object next) throws EntityNotMappedException, IllegalArgumentException, IllegalAccessException, InvocationTargetException, SecurityException, NoSuchMethodException {
		DataSnip dataSnip = DataSnipFactory.none(attribute.getEntity());
		dataSnip.whereId(DSMapper.getBundle(next).getId(next));
		dataSnip.setSnipType(snip.getSnipType());
		snip.fetch(dataSnip);
		provideChildrenLikeParent(dataSnip, next);
	}

	public void updateContentInt(DataSnip snip, ContainerAttribute attribute, Object obj) throws EntityNotMappedException, IllegalArgumentException, IllegalAccessException, InvocationTargetException, SecurityException, NoSuchMethodException {
		EntityBundle contentBundle = DSMapper.getBundle(attribute.getEntity());
		if (contentBundle == null) {
			return;
		}
		String name = snip.entityName;
		if (name.contains("$")) {
			throw new DataScooterException("***Inner class try to save as container: " + name);
		}
		DataSnip result = DataSnipFactory.update(attribute.getEntity());
		for (PersistAttribute attributew : contentBundle.attrArray) {
			result.what(attributew.name, attributew.getValue(obj));
		}
		result.whereId(DSMapper.getBundle(obj).getId(obj));
		snip.fetch(result);
	}

	private void provideParts(EntityBundle bundleIn, DataSnip snip, Object obj) throws EntityNotMappedException, IllegalArgumentException, IllegalAccessException, InvocationTargetException, SecurityException, NoSuchMethodException {
		for (EntityPart part : bundleIn.partList) {
			DataSnip dataSnip = DataSnipFactory.none(part.getEntity());
			dataSnip.setSnipType(snip.getSnipType());
			EntityBundle childBundle = DSMapper.getBundle(part.getEntity());
			EntityBundle parentBundle = DSMapper.getBundle(obj);
			PersistAttribute childAttr = null;
			PersistAttribute parentAttr = null;
			if (part.getJoinAttribute() != null) {
				if (part.isJoinFromChild()) {
					childAttr = childBundle.getAttribute(part.getJoinAttribute());
				} else {
					parentAttr = parentBundle.getAttribute(part.getJoinAttribute());
				}
			} else {
				childAttr = childBundle.getEntityReference(parentBundle.entity);
				parentAttr = parentBundle.getEntityReference(childBundle.entity);
			}
			if (childAttr != null && parentAttr == null) {
				dataSnip.where(part.getJoinAttribute(), DSMapper.getBundle(obj).getId(obj));
			} else if (parentAttr != null && childAttr == null) {
				Object parentIdEx = parentAttr.getValue(obj);
				dataSnip.whereId(parentIdEx);
			} else {
				dataSnip.whereId(DSMapper.getBundle(obj).getId(obj));
			}
			snip.fetch(dataSnip);
		}
	}

	public Object instantiate(Snip retrieve) throws EntityNotMappedException, IllegalArgumentException, SecurityException, InstantiationException, IllegalAccessException, InvocationTargetException, NoSuchMethodException, ParseException {
		if (retrieve.getData() != null && retrieve.getData().length > 0) {
			if (retrieve.getData().length == 1) {
				return instantiateSnip(retrieve);
			} else if (retrieve.getData().length > 1) {
				return instantiateSnipList(retrieve);
			}
		}
		return null;
	}

	/**
	 * Converts a snip to an object through reflection if object metts a JavaBean options e.g. have suitable setters and default constructor
	 * 
	 * @param <T>
	 * @param snip
	 * @return <T>
	 * @throws EntityNotMappedException
	 * @throws ParseException
	 * @throws NoSuchMethodException
	 * @throws InvocationTargetException
	 * @throws IllegalAccessException
	 * @throws InstantiationException
	 * @throws SecurityException
	 * @throws IllegalArgumentException
	 * @throws WrongMethodException
	 */
	public <T> T instantiateSnip(Snip snip) throws EntityNotMappedException, IllegalArgumentException, SecurityException, InstantiationException, IllegalAccessException, InvocationTargetException, NoSuchMethodException, ParseException {
		if (snip == null) {
			return null;
		}
		if (snip.getData() != null && snip.getData().length > 0) {
			if (snip.getData().length > 0) {
				return restore(snip, snip.getData()[0], DSMapper.getBundle(snip.entity));
			}
		}
		return null;
	}

	/**
	 * Converts a snip to a list of ojects through reflection if objects metts a JavaBean options e.g. have suitable setters and default constructor
	 * 
	 * @param <T>
	 * @param snip
	 * @return List<T>
	 * @throws EntityNotMappedException
	 * @throws WrongMethodException
	 */
	@SuppressWarnings("unchecked")
	public <T> List<T> instantiateSnipList(Snip snip) {
		List<T> result = new ArrayList<T>();
		try {
			if ((snip.getData() != null) && (snip.getData().length > 0)) {
				EntityBundle bundle = DSMapper.getBundle(snip.entity);
				for (int a = 0; a < snip.getData().length; a++) {
					result.add((T) restore(snip, snip.getData()[a], bundle));
				}
				if (bundle.hasChild && snip.getFetch() != null && snip.getFetch().length > 0) {
					for (Snip child : snip.getFetch()) {
						result.addAll((Collection<? extends T>) instantiateSnipList(child));
					}
				}
			} else if (snip.getFetch() != null) {
				for (Snip child : snip.getFetch()) {
					result.addAll((Collection<? extends T>) instantiateSnipList(child));
				}
			}
		} catch (Exception e) {
			throw new DataScooterException("Error by snip: " + SnipUtils.snipToString(snip), e);
		}
		return result;
	}

	public <T> DataSnip createInsertSnip(T object, EntityBundle bundleIn) throws EntityNotMappedException, IllegalArgumentException, SecurityException, IllegalAccessException, InvocationTargetException, NoSuchMethodException, InstantiationException, ParseException {
		DataSnip result = new DataSnip(bundleIn.entity, SnipType.INSERT);
		result.setBundle(bundleIn);
		Object[] flush = bundleIn.flush(object, manager);
		result.objectId = flush[0];
		for (int a = 0; a < bundleIn.attrNameArray.length; a++) {
			result.what(bundleIn.attrNameArray[a], flush[a]);
		}
		return result;
	}

	/**
	 * Purposed for a processing a parts - some entities which must be saved into other tables as parent class and not existed as own instances but only consisting of parent object. And not exists
	 * single method for retrieve child entity
	 * 
	 * @param <T>
	 * @param parent
	 * @param parentObject
	 * @param result
	 * @throws EntityNotMappedException
	 * @throws ParseException
	 * @throws InstantiationException
	 * @throws NoSuchMethodException
	 * @throws InvocationTargetException
	 * @throws IllegalAccessException
	 * @throws SecurityException
	 * @throws IllegalArgumentException
	 * @throws WrongMethodException
	 */
	private <T> void flushParts(EntityBundle parent, T parentObject, DataSnip result) throws EntityNotMappedException, IllegalArgumentException, SecurityException, IllegalAccessException, InvocationTargetException, NoSuchMethodException, InstantiationException, ParseException {
		for (EntityPart part : parent.partList) {
			DataSnip createInsertSnip = createInsertSnip(parentObject, DSMapper.getBundle(part.getEntity()));
			// createInsertSnip.setByResult(true);
			result.fetch(createInsertSnip);
		}
	}

	/**
	 * Purposed for processing embedded classes - independed instances which must be saved into same table as parent class - it means one parent can contain only one embedded object of each type. And
	 * there are one method for retrieve child entity
	 * 
	 * @param <T>
	 * @param parent
	 * @param parentObject
	 * @param result
	 * @throws EntityNotMappedException
	 * @throws InvocationTargetException
	 * @throws IllegalAccessException
	 * @throws IllegalArgumentException
	 * @throws ParseException
	 * @throws InstantiationException
	 * @throws NoSuchMethodException
	 * @throws SecurityException
	 * @throws WrongMethodException
	 */
	private <T> void flushEmbedded(EntityBundle parent, T parentObject, DataSnip result) throws EntityNotMappedException, IllegalArgumentException, IllegalAccessException, InvocationTargetException, SecurityException, NoSuchMethodException, InstantiationException, ParseException {
		for (ContainerAttribute attribute : parent.embeddedList) {
			if ((attribute.getPolicy().equals(PersistencePolicy.INSERT) || attribute.getPolicy().equals(PersistencePolicy.ALL))) {
				Object returnObject = attribute.getValue(parentObject);
				if (returnObject == null) {
					continue;
				}
				EntityBundle child = DSMapper.getBundle(attribute.getEntity());
				Object[] flush = child.flush(returnObject, manager);
				PersistAttribute[] attrArray = child.attrArray;
				for (int a = 0; a < flush.length; a++) {
					result.what(attrArray[a].name, flush[a]);
				}
			}
		}
	}

	private <T> void flushContainers(EntityBundle parent, T parentObject, DataSnip result) throws EntityNotMappedException, IllegalArgumentException, IllegalAccessException, InvocationTargetException, SecurityException, NoSuchMethodException {
		Object parentId = parent.getId(parentObject);
		String name = parentObject.getClass().getName();
		for (ContainerAttribute attribute : parent.containerAttributeList) {
			if ((attribute.getPolicy().equals(PersistencePolicy.INSERT) || attribute.getPolicy().equals(PersistencePolicy.ALL))) {
				Object returnObject = attribute.getValue(parentObject);
				if (returnObject == null) {
					continue;
				}
				try {
					EntityBundle child = (attribute.getEntity() == null ? null : DSMapper.getBundle(attribute.getEntity()));
					if (Collection.class.isAssignableFrom(returnObject.getClass())) {
						Collection<?> collection = (Collection<?>) returnObject;
						if (collection.size() > 0) {
							if (child == null) {
								child = provideChildEntity(attribute, collection.iterator().next());
							}
							Iterator<?> iterator = collection.iterator();
							while (iterator.hasNext()) {
								Object next = iterator.next();
								fetchChild(name, result, parentId, attribute, child, next);
							}
						}
					} else if (Map.class.isAssignableFrom(returnObject.getClass())) {
						Map<?, ?> map = (Map<?, ?>) returnObject;
						if (map.size() > 0) {
							if (child == null) {
								child = provideChildEntity(attribute, map.values().iterator().next());
							}
							Iterator<?> iterator = map.values().iterator();
							while (iterator.hasNext()) {
								fetchChild(name, result, parentId, attribute, child, iterator.next());
							}
						}
					} else if (returnObject.getClass().isArray()) {
						Object[] array = (Object[]) returnObject;
						if (array.length > 0) {
							if (child == null) {
								child = provideChildEntity(attribute, array[0]);
							}
							for (int a = 0; a < array.length; a++) {
								fetchChild(name, result, parentId, attribute, child, array[a]);
							}
						}
					} else {
						Object id = child.getId(returnObject);
						if (result.getSnipType().equals(SnipType.INSERT) && id == null) {
							fetchChild(name, result, parentId, attribute, child, returnObject);
						}
					}
				} catch (SecurityException e) {
					e.printStackTrace();
					throw new DataScooterException(e);
				}
			}
		}
	}

	private <T> void fetchChild(String name, DataSnip result, Object parentId, ContainerAttribute attribute, EntityBundle child, Object next) throws EntityNotMappedException {
		DataSnip dataSnip = flush(next);
		if (!isObjectUsed(result, dataSnip.objectId)) {
			result.fetch(dataSnip);
			result.fetch(containerProvider.getContentDataSnip(name, parentId, child, next, attribute.name));
		}
	}

	private boolean isObjectUsed(DataSnip snip, Object objectId) {
		if (snip.objectId != null && snip.objectId.equals(objectId)) {
			return true;
		}
		for (DataSnip dataSnip : snip.fetchList) {
			if (snip.objectId != null && dataSnip.objectId != null && dataSnip.objectId.equals(objectId)) {
				return true;
			}
		}
		if (snip.parent == null) {
			return false;
		}
		return isObjectUsed(snip.parent, objectId);
	}

	private EntityBundle provideChildEntity(ContainerAttribute attribute, Object next) throws EntityNotMappedException, SecurityException, NoSuchMethodException {
		EntityBundle child = DSMapper.getBundle(next.getClass().getName());
		if (child == null) {
			throw new EntityNotMappedException("***Not found mapping for a linked child entity " + attribute.getEntity());
		}
		attribute.setEntity(next.getClass().getName());
		return child;
	}

	private <T> void flushPersistableChildren(EntityBundle parent, T parentObject, DataSnip result) throws EntityNotMappedException, SecurityException, NoSuchMethodException, IllegalArgumentException, IllegalAccessException, InvocationTargetException, InstantiationException, ParseException {
		Object parentId = parent.getId(parentObject);
		for (PersistChildAttribute childBundle : parent.persistableChildList) {
			if ((childBundle.getPolicy().equals(PersistencePolicy.INSERT) || childBundle.getPolicy().equals(PersistencePolicy.ALL))) {
				EntityBundle child = DSMapper.getBundle(childBundle.getEntity());
				if (!childBundle.isResolved()) {
					childBundle.resolveReferrer();
				}
				flushChild(child, childBundle, parentObject, result, parentId);
			}
		}
	}

	private <T> void flushChild(EntityBundle child, PersistChildAttribute childEntityBundle, Object parentObject, DataSnip result, Object parentId) throws EntityNotMappedException, SecurityException, NoSuchMethodException, IllegalArgumentException, IllegalAccessException, InvocationTargetException, InstantiationException, ParseException {
		if (Collection.class.isAssignableFrom(childEntityBundle.getReturnType())) {
			Collection<?> collection = (Collection<?>) childEntityBundle.getValue(parentObject);
			if (collection != null && collection.size() > 0) {
				for (Iterator<?> iterator = collection.iterator(); iterator.hasNext();) {
					putPart(child, result, iterator.next(), parentObject, parentId, childEntityBundle);
				}
			}
		} else if (childEntityBundle.getReturnType().isArray()) {
			Object[] array = (Object[]) childEntityBundle.getValue(parentObject);
			if (array != null && array.length > 0) {
				for (int a = 0; a < array.length; a++) {
					putPart(child, result, array[a], parentObject, parentId, childEntityBundle);
				}
			}
		} else {
			Object returnObject = childEntityBundle.getValue(parentObject);
			if (returnObject != null) {
				putPart(child, result, returnObject, parentObject, parentId, childEntityBundle);
			}
		}
	}

	private void putPart(EntityBundle child, DataSnip result, Object childObject, Object parentObject, Object parentId, PersistChildAttribute childBundle) throws EntityNotMappedException, IllegalArgumentException, IllegalAccessException, InvocationTargetException, SecurityException, NoSuchMethodException, InstantiationException, ParseException {
		PersistAttribute childReference = childBundle.getReference();
		if (childBundle.isParent()) {
			Object parentIdEx = childReference.getValue(parentObject);
			if (parentIdEx == null) {
				childReference.setValue(parentObject, child.getId(childObject));
			}
		} else {
			Object parentIdEx = childReference.getValue(childObject);
			if (parentIdEx == null) {
				childReference.setValue(childObject, parentId);
			}
		}
		result.fetch(flush(childObject));
	}

	private <T> T restore(Snip snip, Object[] array, EntityBundle bundle) throws InstantiationException, IllegalAccessException, EntityNotMappedException, IllegalArgumentException, SecurityException, InvocationTargetException, NoSuchMethodException, ParseException {
		T restore = bundle.restore(snip, array);
		if (bundle.hasPersistableChild) {
			instantiatePersistedChildren(restore, array, bundle);
		}
		if (bundle.hasContainers) {
			instantiateContainers(restore, array, bundle);
		}
		if (bundle.hasEmbedded) {
			processEmbedded(restore, array, bundle, snip);
		}
		if (bundle.hasParts) {
			processParts(restore, bundle, snip);
		}
		return restore;
	}

	private <T> void processParts(T obj, EntityBundle parent, Snip snip) throws EntityNotMappedException, InstantiationException, IllegalAccessException, SecurityException, IllegalArgumentException, NoSuchMethodException, InvocationTargetException, ParseException {
		if (snip.getFetch() == null) {
			return;
		}
		for (EntityPart part : parent.partList) {
			EntityBundle partBundle = DSMapper.getBundle(part.getEntity());
			for (Snip fetch : snip.getFetch()) {
				if (part.getEntity().equals(fetch.entity) && fetch.getData().length > 0) {
					for (int a = 1; a < fetch.getData()[0].length; a++) {
						if (fetch.getData()[0][a] == null) {
							continue;
						}
						PersistAttribute attribute = partBundle.getAttributeByColumn(fetch.fields[a]);
						if (attribute != null) {
							attribute.setValue(obj, fetch.getData()[0][a]);
						}
					}
				}
			}
		}
	}

	private <T> void processEmbedded(T obj, Object[] array, EntityBundle parent, Snip snip) throws EntityNotMappedException, InstantiationException, IllegalAccessException, IllegalArgumentException, SecurityException, InvocationTargetException, NoSuchMethodException, ParseException {
		for (ContainerAttribute attribute : parent.embeddedList) {
			if ((attribute.getPolicy().equals(PersistencePolicy.SELECT) || attribute.getPolicy().equals(PersistencePolicy.ALL))) {
				EntityBundle bundle = DSMapper.getBundle(attribute.getEntity());
				attribute.setValue(obj, bundle.restore(snip, array));
			}
		}
	}

	private <T> void instantiateContainers(T obj, Object[] array, EntityBundle parent) throws EntityNotMappedException, IllegalArgumentException, SecurityException, IllegalAccessException, InvocationTargetException, NoSuchMethodException, InstantiationException, ParseException {
		for (ContainerAttribute attribute : parent.containerAttributeList) {
			if ((attribute.getPolicy().equals(PersistencePolicy.SELECT) || attribute.getPolicy().equals(PersistencePolicy.ALL)) && !attribute.isFetchLazy()) {
				List<T> content = containerProvider.getContent(obj, attribute.name);
				if (content.size() > 0) {
					fillUpResultChild2(obj, attribute, content);
				}
			}
		}
	}

	@SuppressWarnings({
			"unchecked", "rawtypes"
	})
	private <T> void fillUpResultChild2(T obj, ContainerAttribute child, List<T> list) throws EntityNotMappedException, IllegalArgumentException, SecurityException, IllegalAccessException, InvocationTargetException, NoSuchMethodException, InstantiationException, ParseException {
		Class<?> cls = child.getReturnType();
		if (List.class.isAssignableFrom(cls)) {
			child.setValue(obj, list);
		} else if (Set.class.isAssignableFrom(cls)) {
			Set emptySet = new HashSet();
			emptySet.addAll(list);
			child.setValue(obj, emptySet);
		} else if (Map.class.isAssignableFrom(cls)) {
			PersistAttribute mapAttribute = null;
			String mapKeyAttribute = child.getMapKeyAttribute();
			if (mapKeyAttribute == null) {
				mapAttribute = DSMapper.getBundle(child.getEntity()).id;
			} else {
				mapAttribute = DSMapper.getBundle(child.getEntity()).getAttribute(mapKeyAttribute);
			}
			Map map = new HashMap();
			for (T item : list) {
				map.put(mapAttribute.getValue(item), item);
			}
			child.setValue(obj, map);
		} else if (cls.isArray()) {
			child.setValue(obj, list.toArray());
		} else {
			child.setValue(obj, list.size() > 0 ? list.get(0) : null);
		}
	}

	@SuppressWarnings({
			"unchecked", "rawtypes"
	})
	private <T> void instantiatePersistedChildren(T obj, Object[] array, EntityBundle parent) throws EntityNotMappedException, IllegalArgumentException, IllegalAccessException, InvocationTargetException, SecurityException, NoSuchMethodException, InstantiationException, ParseException {
		for (PersistChildAttribute child : parent.persistableChildList) {
			if ((child.getPolicy().equals(PersistencePolicy.SELECT) || child.getPolicy().equals(PersistencePolicy.ALL))) {
				if (child.getArrayIndex() >= array.length || array[child.getArrayIndex()] == null) {
					continue;
				}
				Snip snip = manager.getSnipCashe().getCachedSnip(child.resolveQuery(), manager.getTransactionId());
				List list = new ArrayList();
				list.add(array[child.getArrayIndex()]);
				snip.setData(manager.getBuilderInt().customParamTypes(list));
				List<T> list1 = instantiateSnipList(manager.retrieve(snip));
				fillUpResultChild(obj, child, list1);
			}
		}
	}

	@SuppressWarnings({
			"unchecked", "rawtypes"
	})
	private <T> void fillUpResultChild(T obj, PersistChildAttribute child, List<T> list) throws EntityNotMappedException, IllegalArgumentException, IllegalAccessException, InvocationTargetException, SecurityException, NoSuchMethodException, InstantiationException, ParseException {
		Class<?> cls = child.getReturnType();
		if (List.class.isAssignableFrom(cls)) {
			child.setValue(obj, list);
		} else if (Set.class.isAssignableFrom(cls)) {
			child.setValue(obj, new HashSet(list));
		} else if (Map.class.isAssignableFrom(cls)) {
			PersistAttribute attribute = DSMapper.getBundle(child.getEntity()).getAttribute(child.getMapKeyAttribute());
			Map map = new HashMap();
			for (T item : list) {
				map.put(attribute.getValue(item), item);
			}
			child.setValue(obj, map);
		} else if (cls.isArray()) {
			child.setValue(obj, list.toArray());
		} else {
			child.setValue(obj, list.size() >= 1 ? list.get(0) : null);
		}
	}

	public <T> List<T> getContent(Object container, String containerAttribute) {
		return containerProvider.getContent(container, containerAttribute);
	}

	public void saveContent(Object container, String containerAttribute, Object... content) throws EntityNotMappedException, IllegalArgumentException, IllegalAccessException, InvocationTargetException, SecurityException, NoSuchMethodException {
		containerProvider.saveContent(container, containerAttribute, content);
	}

	public void removeContent(Object container, String containerAttribute, Object... content) {
		containerProvider.removeContent(container, containerAttribute, content);
	}

	public ContainerProvider getCrossProvider() {
		return containerProvider;
	}
}
